package com.yehun.shiro;

import com.yehun.entity.Role;
import com.yehun.entity.Route;
import com.yehun.entity.User;
import com.yehun.enums.UserTypeEnum;
import com.yehun.service.RoleService;
import com.yehun.service.RouteService;
import com.yehun.service.UserService;
import com.yehun.utils.BitOperUtils;
import org.apache.calcite.linq4j.Linq4j;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

public class RolesAuthorizationFilter extends AuthorizationFilter {

    private final static Logger logger = LoggerFactory.getLogger(RolesAuthorizationFilter.class);

    private final UserService userService;
    private final RoleService roleService;
    private final RouteService routeService;

    @Autowired
    public RolesAuthorizationFilter(UserService userService, RoleService roleService, RouteService routeService) {
        this.userService = userService;
        this.roleService = roleService;
        this.routeService = routeService;
    }

    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        String currentUrl = ((HttpServletRequest) request).getRequestURI();
        logger.info("currentUrl={}", currentUrl);
        Subject subject = super.getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;
        boolean isAllow = false;
        if (rolesArray == null || rolesArray.length == 0) {
            isAllow = true;
        } else {
            Set<String> roles = CollectionUtils.asSet(rolesArray);
            boolean[] hasRoles = subject.hasRoles(new LinkedList<>(roles));
            for (boolean hasRole : hasRoles) {
                if (hasRole) {
                    isAllow = true;
                    break;
                }
            }
        }
        //只有角色验证通过才能进一步验证权限
        if (isAllow) {
            //ShiroUser shiroUser = (ShiroUser) subject.getPrincipals().getPrimaryPrincipal();
            Object currentUserObject = subject.getPrincipal();
            if (currentUserObject == null) {
                return false;
            }
            ShiroUser shiroUser = (ShiroUser) currentUserObject;
            User currentUser = userService.getByUserId(shiroUser.getUserId());
            if (currentUser == null) {
                return false;
            }
            try {
                if (currentUser.isAdministrator()) {
                    //去除后台路由的前缀
                    currentUrl = currentUrl.replace("/admin", "");
                    List<Route> routes = routeService.listAll();
                    final String currentUrlFinal = currentUrl;
                    Route routeTmp = Linq4j.asEnumerable(routes).where(r -> Objects.equals(currentUrlFinal, r.getUrl())).first();
                    //找不到当前路由默认通过
                    if (routeTmp == null) {
                        logger.info("路由[{}]未在权限控制中", currentUrlFinal);
                        return true;
                    }
                    //用户拥有此权限则通过
                    if (BitOperUtils.exists(currentUser.getPermission(), routeTmp.getValue())) {
                        logger.info("用户[{}]权限通过", currentUser.getName());
                        return true;
                    }
                    //用户所有角色拥有此权限则通过
                    Role currentUserRole = roleService.get(currentUser.getRoleId());
                    if (currentUserRole != null && BitOperUtils.exists(currentUserRole.getPermission(), routeTmp.getValue())) {
                        logger.info("用户[{}]所在[{}]角色权限通过", currentUser.getName(), currentUserRole.getName());
                        return true;
                    }
                } else {
                    subject.logout();
                }
            } catch (Exception e) {
                logger.error("RolesAuthorizationFilter=", e);
            }
        }
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        return super.onAccessDenied(request, response);
    }


}
